package me.muphy.frp;

import java.io.*;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;

public class TcpFrpServer extends Thread {

    private final ServerSocket intranetServer;
    private final ServerSocket extranetServer;

    private static InputStream intranetInputStream = null; //服务端输入流
    private static OutputStream intranetOutputStream = null;  //服务端输出流
    //使用线程处理收到的请求
    private volatile InputStream extranetInputStream = null;
    private volatile OutputStream extranetOutputStream = null;
    private volatile Socket extranetClient = null;
    private volatile Socket intranetClient = null;


    public TcpFrpServer(int intranetPort, int extranetPort) throws IOException {
        intranetServer = new ServerSocket(intranetPort);
        extranetServer = new ServerSocket(extranetPort);
        System.out.println("内网端口：" + intranetPort + "，外网端口：" + extranetPort);
    }

    public static void main(String[] args) throws IOException {
        System.out.println("usage: java ProxyServer [内网:6010] [外网:6011]");
        int intranetPort = 48981;
        int extranetPort = 48982;
        if (args.length > 0) {
            intranetPort = Integer.parseInt(args[0]);
        }
        if (args.length > 1) {
            extranetPort = Integer.parseInt(args[1]);
        }
        new TcpFrpServer(intranetPort, extranetPort).start();
    }

    @Override
    public void run() {
        // 线程运行函数
        if (!getIntranetClient()) {
            System.out.println("等待内网服务连接失败");
            return;
        }
        Executors.newFixedThreadPool(1).execute(() -> {
            while (true) {
                if (intranetClient == null || intranetClient.isClosed()) {
                    if (!getIntranetClient()) {
                        System.out.println("等待内网服务连接失败");
                        break;
                    }
                }
                byte[] buf = new byte[1024];
                //try {
                while (true) {
                    int len = 0;
                    try {
                        len = intranetInputStream.read(buf, 0, buf.length);
                    } catch (IOException e) {
                        e.printStackTrace();
                        close(intranetInputStream, intranetOutputStream, intranetClient);
                        intranetClient = null;
                        break;
                    }
                    if (len < 0) {
                        close(intranetInputStream, intranetOutputStream, intranetClient);
                        intranetClient = null;
                        break;
                    }

                    String suitableString = getSuitableString(buf, len);
                    System.out.println("内网：" + suitableString);
                    if (extranetClient == null || extranetClient.isClosed()) {
                        continue;
                    }
                    if ("close".equals(suitableString)) {
                        close(extranetInputStream, extranetOutputStream, extranetClient);
                        extranetClient = null;
                        continue;
                    }
                    boolean close = suitableString.endsWith("close");
                    if (close) {
                        try {
                            extranetOutputStream.write(buf, 0, len - 5);
                            extranetOutputStream.flush();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                            close(extranetInputStream, extranetOutputStream, extranetClient);
                            extranetClient = null;
                            continue;
                        }
                    }
                    try {
                        extranetOutputStream.write(buf, 0, len);
                        extranetOutputStream.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                        close(extranetInputStream, extranetOutputStream, extranetClient);
                        byte[] bytes = "close".getBytes();
                        try {
                            intranetOutputStream.write(bytes, 0, bytes.length);
                            intranetOutputStream.flush();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                            close(intranetInputStream, intranetOutputStream, intranetClient);
                            break;
                        }
                    }
                }
            }
        });

        while (true) {
            byte[] buf = new byte[1024];
            try {
                extranetClient = extranetServer.accept();
                System.out.println("外网连接：" + extranetClient.getRemoteSocketAddress());
                //使用线程处理收到的请求
                extranetInputStream = extranetClient.getInputStream(); //客户端输入流
                extranetOutputStream = extranetClient.getOutputStream(); //客户端输出流
                int len;
                while ((len = extranetInputStream.read(buf, 0, buf.length)) > -1) {
                    System.out.println("外网：" + getSuitableString(buf, len));
                    intranetOutputStream.write(buf, 0, len);
                    intranetOutputStream.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            close(extranetClient, extranetInputStream, extranetOutputStream);
            byte[] bytes = "close".getBytes();
            try {
                intranetOutputStream.write(bytes, 0, bytes.length);
                intranetOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized boolean getIntranetClient() {
        if (intranetClient != null && !intranetClient.isClosed()) {
            return false;
        }
        try {
            intranetClient = intranetServer.accept();
            System.out.println("内网连接：" + intranetClient.getRemoteSocketAddress());
            intranetInputStream = intranetClient.getInputStream();
            intranetOutputStream = intranetClient.getOutputStream();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            close(intranetInputStream, intranetOutputStream, intranetClient);
        }
        return false;
    }

    public static String getSuitableString(byte[] bytes, int len) {
        if (bytes == null || bytes.length == 0) {
            return "";
        }
        byte[] buffer = new byte[len];
        System.arraycopy(bytes, 0, buffer, 0, len);
        String suitableParseString = new String(buffer);
        if (true) {
            return suitableParseString;
        }
        if (Pattern.compile("^[0-9a-zA-Z\\-\\+\\.\\*\\s\\!\\@\\(\\)\\#\\=\\?\\,\\$~\\|\\&\\^\\_]*$").matcher(suitableParseString).matches()) {
            return "ASCII>" + suitableParseString;
        }
        suitableParseString = new BigInteger(1, buffer).toString(16);
        if (Pattern.compile("^[0-9a-zA-Z\\-\\+\\.\\*\\s\\!\\@\\(\\)\\#\\=\\?\\,\\$~\\|\\&\\^\\_]*$").matcher(suitableParseString).matches()) {
            suitableParseString = toHexFormal(suitableParseString);
            return "HEX>" + suitableParseString;
        }
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < buffer.length; i++) {
            stringBuilder.append(String.valueOf(buffer[i]));
            if (i < buffer.length - 1) {
                stringBuilder.append(" ");
            }
        }
        return "UNKNOWN>" + stringBuilder.toString();
    }

    public static String toHexFormal(String src) {
        if (src == null) {
            return "";
        }
        src = src.replaceAll("\\s+", "").replaceAll("[^0-9a-fA-FX]", "");
        char[] chars = src.toCharArray();
        StringBuilder sb = new StringBuilder();
        int i = 0, len = chars.length % 2 == 0 ? chars.length : chars.length - 1;
        for (; i < len; i++) {
            sb.append(chars[i]);
            sb.append(chars[++i]);
            sb.append(" ");
        }
        if (i < chars.length) {
            sb.append(chars[i]);
        }
        return sb.toString().trim().toUpperCase();
    }

    /**
     * 关闭所有流
     */
    private static void close(Closeable... closeables) {
        if (closeables != null) {
            for (int i = 0; i < closeables.length; i++) {
                if (closeables[i] != null) {
                    try {
                        closeables[i].close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
